package service

import (
	"context"
	"fmt"
	"gen-code-v2/internal/consts"
	"gen-code-v2/internal/model"
	"gen-code-v2/internal/model/entity"
	"gen-code-v2/internal/service/internal/dao"
	utils "gen-code-v2/utility"
	"github.com/blastrain/vitess-sqlparser/sqlparser"
	"github.com/blastrain/vitess-sqlparser/tidbparser/ast"
	"github.com/blastrain/vitess-sqlparser/tidbparser/parser"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"os"
	"reflect"
	"strings"
)

type (
	// sDatabase is service struct of module Table.
	sTable struct{}
)

var (
	// insDatabase is the instance of service Table.
	insTable = sTable{}
)

// Table returns the interface of Table service.
func Table() *sTable {
	return &insTable
}

func (s *sTable) PageList(ctx context.Context, in model.GenTableReq) (total int, list []*entity.GenTable, err error) {
	daoModel := dao.GenTable.Ctx(ctx)
	if !reflect.DeepEqual(in, model.GenTableReq{}) {
		if in.TableName != "" {
			daoModel = daoModel.WhereLike(dao.GenTable.Columns().TableName, "%"+in.TableName+"%")
		}
		if in.TableComment != "" {
			daoModel = daoModel.WhereLike(dao.GenTable.Columns().TableComment, "%"+in.TableComment+"%")
		}
		if !in.BeginTime.IsZero() && !in.EndTime.IsZero() {
			daoModel = daoModel.WhereBetween(dao.GenTable.Columns().CreateTime, in.BeginTime, in.EndTime.EndOfDay())
		}
		total, err = daoModel.Count()
		if err != nil {
			g.Log().Error(ctx, err)
			err = gerror.New("获取总行数失败")
			return
		}
		if total == 0 {
			list = make([]*entity.GenTable, 0)
			return total, list, nil
		}
		err = daoModel.Page(in.PageNum, in.PageSize).OrderAsc(dao.GenTable.Columns().TableId).Scan(&list)
		if err != nil {
			g.Log().Error(ctx, err)
			err = gerror.New("获取数据失败")
		}
	}
	return
}

func (s *sTable) DBPageList(ctx context.Context, in model.GenTableReq) (total int, list []*entity.GenTable, err error) {
	if s.getDbDriver() != "mysql" {
		err = gerror.New("代码生成暂时只支持mysql数据库")
		return
	}
	var db gdb.DB = nil
	sql := " from information_schema.tables where table_schema = (select database())"
	if in.Group != gdb.DefaultGroupName {
		var dbConfig *entity.GenDatabase
		err := dao.GenDatabase.Ctx(ctx).Scan(&dbConfig)
		if err != nil {
			return 0, nil, err
		}
		gdb.AddConfigNode(in.Group, gdb.ConfigNode{
			Host:   dbConfig.Host,
			Port:   dbConfig.Port,
			User:   dbConfig.User,
			Pass:   dbConfig.Pass,
			Name:   dbConfig.Name,
			Type:   dbConfig.Type,
			Debug:  true,
			DryRun: true,
		})
		db, err = gdb.Instance(in.Group)
		if err != nil {
			return 0, nil, err
		}

	} else {
		db = g.DB()
		sql += " and table_name NOT IN (select table_name from " + dao.GenTable.Table() + ") "

	}
	if !reflect.DeepEqual(in, model.GenTableReq{}) {
		if in.TableName != "" {
			sql += gdb.FormatSqlWithArgs(" and lower(table_name) like lower(?)", []interface{}{"%" + in.TableName + "%"})
		}

		if in.TableComment != "" {
			sql += gdb.FormatSqlWithArgs(" and lower(table_comment) like lower(?)", []interface{}{"%" + in.TableComment + "%"})
		}
	}
	countSql := "select count(1) " + sql
	total, err = db.GetCount(ctx, countSql)
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("读取总表数失败")
		return
	}
	if total == 0 {
		list = make([]*entity.GenTable, 0)
		return total, list, nil
	}
	sql = "table_name, table_comment, create_time, update_time " + sql
	page := (in.PageNum - 1) * in.PageSize
	sql += " order by create_time desc,table_name asc limit  " + gconv.String(page) + "," + gconv.String(in.PageSize)
	err = db.GetScan(ctx, &list, "select "+sql)
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("读取数据失败")
	}
	return
}

//获取数据库驱动类型
func (s *sTable) getDbDriver() string {
	config := g.DB().GetConfig()
	return gstr.ToLower(config.Type)
}

func (s *sTable) Create(ctx context.Context, sql string) (err error) {
	parse, err := parser.New().Parse(sql, "", "")
	if err != nil {
		return
	}
	for _, stmtNode := range parse {
		switch stmtNode := stmtNode.(type) {
		case *ast.CreateTableStmt:
			err := s.create(ctx, stmtNode.Text())
			if err != nil {
				return err
			}
		}
	}
	return
}

func (s *sTable) create(ctx context.Context, sql string) (err error) {
	stmt, err := sqlparser.Parse(sql)
	if err != nil {
		return gerror.New("请输入建表语句")
	}
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		return err
	}
	switch stmt := stmt.(type) {
	case *sqlparser.CreateTable:
		tableName := stmt.NewName.Name.String()
		count, err := tx.Model(dao.GenTable.Table()).Where(dao.GenTable.Columns().TableName, tableName).Count()
		if err != nil {
			return err
		}
		if count > 0 {
			return gerror.New(fmt.Sprintf("表%s已存在！", tableName))
		}
		var table = &entity.GenTable{
			TableName: tableName,
		}
		for _, option := range stmt.Options {
			if option.Type == sqlparser.TableOptionComment {
				table.TableComment = option.StrValue
			}
		}
		s.InitTable(ctx, table)
		id, err := tx.Model(dao.GenTable.Table()).FieldsEx(dao.GenTable.Columns().TableId).InsertAndGetId(table)
		if err != nil {
			return err
		}
		table.TableId = id
		var columns = make([]entity.GenTableColumn, 0)
		for _, column := range stmt.Columns {
			var genTableColumn = &entity.GenTableColumn{
				ColumnName: column.Name,
				ColumnType: column.Type,
				CreateTime: gtime.Now(),
			}
			for _, option := range column.Options {

				switch option.Type {
				case sqlparser.ColumnOptionPrimaryKey:
					genTableColumn.IsPk = "1"
				case sqlparser.ColumnOptionComment:
					genTableColumn.ColumnComment = strings.Trim(option.Value, "\"")
				}
			}
			TableColumn().InitColumnField(genTableColumn, table)
			columns = append(columns, *genTableColumn)
		}

		_, err = tx.Model(dao.GenTableColumn.Table()).FieldsEx(dao.GenTableColumn.Columns().ColumnId).Insert(columns)
		if err != nil {
			_ = tx.Rollback()
			return gerror.New("保存列数据失败")
		}
	default:
		return gerror.New("请输入建表语句")
	}
	return tx.Commit()
}

// InitTable 初始化表信息
func (s *sTable) InitTable(ctx context.Context, table *entity.GenTable) {
	table.ClassName = s.ConvertClassName(ctx, table.TableName)
	packageName, _ := g.Cfg().Get(ctx, "gen.packageName")
	table.PackageName = packageName.String()
	moduleName, _ := g.Cfg().Get(ctx, "gen.moduleName")
	table.ModuleName = moduleName.String()
	table.BusinessName = s.GetBusinessName(ctx, table.TableName)
	table.FunctionName = table.TableComment
	author, _ := g.Cfg().Get(ctx, "gen.author")
	table.GenType = "0"
	table.GenPath = "/"
	table.FunctionAuthor = author.String()
	table.TplCategory = "crud"
	table.CreateTime = gtime.Now()
	table.UpdateTime = table.CreateTime
}

// ConvertClassName 表名转换成类名
func (s *sTable) ConvertClassName(ctx context.Context, tableName string) string {
	return gstr.CaseCamel(s.removeTablePrefix(ctx, tableName))
}

// GetBusinessName 获取业务名
func (s *sTable) GetBusinessName(ctx context.Context, tableName string) string {
	return s.removeTablePrefix(ctx, tableName)
}

//删除表前缀
func (s *sTable) removeTablePrefix(ctx context.Context, tableName string) string {
	autoRemovePre, _ := g.Cfg().Get(ctx, "gen.autoRemovePre")
	tablePrefix, _ := g.Cfg().Get(ctx, "gen.tablePrefix")
	if autoRemovePre.Bool() && tablePrefix.String() != "" {
		searchList := strings.Split(tablePrefix.String(), ",")
		for _, str := range searchList {
			if strings.HasPrefix(tableName, str) {
				tableName = strings.Replace(tableName, str, "", 1) //注意，只替换一次
			}
		}
	}
	return tableName
}

func (s *sTable) SelectDbTableListByNames(ctx context.Context, in model.TableImportSaveReq) (list []*entity.GenTable, err error) {
	if s.getDbDriver() != "mysql" {
		return nil, gerror.New("代码生成只支持mysql数据库")
	}
	var db gdb.DB = nil
	if in.Group != gdb.DefaultGroupName {
		var dbConfig *entity.GenDatabase
		err := dao.GenDatabase.Ctx(ctx).Scan(&dbConfig)
		if err != nil {
			return nil, err
		}
		gdb.AddConfigNode(in.Group, gdb.ConfigNode{
			Host:   dbConfig.Host,
			Port:   dbConfig.Port,
			User:   dbConfig.User,
			Pass:   dbConfig.Pass,
			Name:   dbConfig.Name,
			Type:   dbConfig.Type,
			Debug:  true,
			DryRun: true,
		})
		db, err = gdb.Instance(in.Group)
		if err != nil {
			return nil, err
		}
	} else {
		db = g.DB()
	}
	sql := "select * from information_schema.tables where table_schema = (select database()) "
	if len(in.Tables) > 0 {
		tbs := gstr.TrimRight(gstr.Repeat("?,", len(in.Tables)), ",")
		sql += " and " + gdb.FormatSqlWithArgs("table_name in ("+tbs+")", gconv.SliceAny(in.Tables))
	}
	err = db.GetScan(ctx, &list, sql)
	if err != nil {
		g.Log().Error(ctx, err)
		return nil, gerror.New("获取表格信息失败")
	}
	return
}

func (s *sTable) ImportGenTable(ctx context.Context, group string, tableList []*entity.GenTable) (err error) {
	if tableList != nil {
		tx, err := g.DB().Begin(ctx)
		if err != nil {
			return err
		}
		for _, table := range tableList {
			tableName := table.TableName
			count, err := tx.Model(dao.GenTable.Table()).Where(dao.GenTable.Columns().TableName, tableName).Count()
			if err != nil || count > 0 {
				return gerror.Newf("%s已存在", tableName)
			}
			s.InitTable(ctx, table)
			tmpId, err := tx.Model(dao.GenTable.Table()).FieldsEx(dao.GenTable.Columns().TableId).InsertAndGetId(table)
			if err != nil || tmpId <= 0 {
				err = tx.Rollback()
				return gerror.New("保存数据失败")
			}

			table.TableId = tmpId

			// 保存列信息
			genTableColumns, err := TableColumn().SelectDbTableColumnsByName(ctx, group, tableName)

			if err != nil || len(genTableColumns) <= 0 {
				tx.Rollback()
				return gerror.New("获取列数据失败")
			}
			for _, column := range genTableColumns {
				TableColumn().InitColumnField(column, table)
				_, err = tx.Model(dao.GenTableColumn.Table()).FieldsEx(dao.GenTableColumn.Columns().ColumnId).Insert(column)
				if err != nil {
					err = tx.Rollback()
					if err != nil {
						return err
					}
					return gerror.New("保存列数据失败")
				}
			}
		}
		return tx.Commit()
	} else {
		return gerror.New("参数错误")
	}
}

func (s *sTable) SelectGenTableById(ctx context.Context, tableId int) (table *model.GenTableDTO, err error) {
	err = dao.GenTable.Ctx(ctx).Where(dao.GenTable.Columns().TableId, tableId).Scan(&table)
	if err != nil {
		return nil, err
	}
	var columns []*entity.GenTableColumn
	err = dao.GenTableColumn.Ctx(ctx).
		Where(dao.GenTableColumn.Columns().TableId, tableId).Scan(&columns)
	if err != nil {
		return nil, err
	}
	table.Columns = columns
	s.setTableFromOptions(table)
	return
}

func (s *sTable) SelectGenTableAll(ctx context.Context) (table []*model.GenTableDTO, err error) {
	err = dao.GenTable.Ctx(ctx).Scan(&table)
	if err != nil {
		return nil, err
	}
	var columns []*entity.GenTableColumn
	err = dao.GenTableColumn.Ctx(ctx).Scan(&columns)
	if err != nil {
		return nil, err
	}
	for _, tableDTO := range table {
		for _, column := range columns {
			if column.TableId == tableDTO.TableId {
				tableDTO.Columns = append(tableDTO.Columns, column)
			}
		}
	}
	return table, nil
}

func (s *sTable) setTableFromOptions(entity *model.GenTableDTO) {
	if entity != nil && entity.Options != "" {
		json := gjson.New(entity.Options)
		entity.TreeCode = json.Get(consts.TreeCode).String()
		entity.TreeName = json.Get(consts.TreeName).String()
		entity.TreeParentCode = json.Get(consts.TreeParentCode).String()
		entity.ParentMenuId = json.Get(consts.ParentMenuName).String()
		entity.ParentMenuName = json.Get(consts.ParentMenuId).String()
	}
}

func (s *sTable) SaveEdit(ctx context.Context, req *model.GenTableDTO) (err error) {
	if reflect.DeepEqual(req, model.GenTableReq{}) {
		err = gerror.New("参数错误")
		return
	}
	var table *entity.GenTable
	err = dao.GenTable.Ctx(ctx).Where(dao.GenTable.Columns().TableId, req.TableId).Scan(&table)
	if err != nil || table == nil {
		err = gerror.New("数据不存在")
		return
	}
	if req.TableName != "" {
		table.TableName = req.TableName
	}
	if req.TableComment != "" {
		table.TableComment = req.TableComment
	}
	if req.SubTableName != "" {
		table.SubTableName = req.SubTableName
	}
	if req.SubTableFkName != "" {
		table.SubTableFkName = req.SubTableFkName
	}
	if req.ClassName != "" {
		table.ClassName = req.ClassName
	}
	if req.TplCategory != "" {
		table.TplCategory = req.TplCategory
	}
	if req.PackageName != "" {
		table.PackageName = req.PackageName
	}
	if req.ModuleName != "" {
		table.ModuleName = req.ModuleName
	}

	if req.BusinessName != "" {
		table.BusinessName = req.BusinessName
	}
	if req.FunctionName != "" {
		table.FunctionName = req.FunctionName
	}
	if req.FunctionAuthor != "" {
		table.FunctionAuthor = req.FunctionAuthor
	}
	table.GenType = req.GenType
	if req.GenPath != "" {
		table.GenPath = req.GenPath
	}
	if req.Params != "" {
		table.Options = req.Params
	}
	if req.Remark != "" {
		table.Remark = req.Remark
	}
	table.UpdateTime = gtime.Now()
	var options = gconv.Map(req.Params)

	var tx *gdb.TX
	tx, err = g.DB().Begin(ctx)
	if err != nil {
		return
	}

	_, err = tx.Model(dao.GenTable.Table()).Where(dao.GenTable.Columns().TableId, table.TableId).Update(table)
	if err != nil {
		err = tx.Rollback()
		return
	}

	//保存列数据
	if req.Columns != nil {
		for _, column := range req.Columns {
			if column.ColumnId > 0 {
				if tc, e := options[consts.TreeParentCode]; options != nil && e && tc != "" && tc == column.JavaField {
					column.IsQuery = "0"
					column.IsList = "0"
					column.HtmlType = "select"
				}
				_, err = tx.Model(dao.GenTableColumn.Table()).Where(dao.GenTableColumn.Columns().ColumnId, column.ColumnId).Update(column)
				if err != nil {
					err = tx.Rollback()
					return
				}
			}
		}
	}
	err = tx.Commit()

	return
}

func (s *sTable) Delete(ctx context.Context, ids []int) (err error) {
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		g.Log().Error(ctx, err)
		return gerror.New("开启删除事务出错")
	}
	_, err = tx.Model(dao.GenTable.Table()).WhereIn(dao.GenTable.Columns().TableId, ids).Delete()
	if err != nil {
		g.Log().Error(ctx, err)
		err = tx.Rollback()
		return gerror.New("删除表格数据失败")
	}
	_, err = tx.Model(dao.GenTableColumn.Table()).WhereIn(dao.GenTableColumn.Columns().TableId, ids).Delete()
	if err != nil {
		g.Log().Error(ctx, err)
		err = tx.Rollback()
		return gerror.New("删除表格字段数据失败")
	}
	err = tx.Commit()
	return err
}

func (s *sTable) PreviewCode(ctx context.Context, tableId int) (data g.MapStrStr, err error) {
	table, err := s.SelectGenTableById(ctx, tableId)
	if err != nil {
		return
	}
	dataMap, err := s.generatorCode(ctx, table)
	if err != nil {
		return
	}
	//var templateName = index.substring(index.lastIndexOf("/") + 1, index.length)
	//data = map[string]string{}
	//for key, item := range dataMap {
	//	index := gstr.PosRRune(key, "/")
	//	name := gstr.SubStr(key, index+1)
	//	data[name] = item
	//}
	data = dataMap
	return

}

func (s *sTable) generatorCode(ctx context.Context, table *model.GenTableDTO) (data g.MapStrStr, err error) {
	data = map[string]string{}
	//设置主子表信息
	s.setSubTable(ctx, table)
	//设置主键列信息
	s.setPkColumn(table)
	view := utils.InitView()
	//设置模板变量
	tplContext := utils.PrepareContext(table)
	// 获取 BasePath 文件夹下所有tpl文件
	templates, err := utils.GetAllTplFile(consts.BasePath, table.TplCategory, nil)
	if err != nil {
		return nil, err
	}
	for _, template := range templates {
		fileName := utils.GetFileName(template, table)
		if tmpDao, err := view.Parse(ctx, template, tplContext); err == nil {
			daoValue, _ := utils.TrimBreak(tmpDao)
			data[fileName] = daoValue
		} else {
			g.Log().Error(ctx, err)
		}
	}
	if consts.TplSub == table.TplCategory {
		subData, err := s.generatorCode(ctx, table.SubTable)
		if err != nil {
			return nil, err
		}
		for template, code := range subData {
			data[template] = code
		}
	}
	return
}

func (s *sTable) setSubTable(ctx context.Context, table *model.GenTableDTO) {
	subTableName := table.SubTableName
	if subTableName != "" && consts.TplSub == table.TplCategory {
		subTable, err := s.SelectGenTableByName(ctx, subTableName)
		if err != nil {
			return
		}
		table.SubTable = subTable
	}
}

func (s *sTable) setPkColumn(table *model.GenTableDTO) {
	for _, column := range table.Columns {
		if "1" == column.IsPk {
			table.PkColumn = column
			break
		}
	}
	if table.PkColumn == nil {
		table.PkColumn = table.Columns[0]
	}
	if consts.TplSub == table.TplCategory {
		for _, column := range table.SubTable.Columns {
			if "1" == column.IsPk {
				table.SubTable.PkColumn = column
				break
			}
		}
		if table.SubTable.PkColumn == nil {
			table.SubTable.PkColumn = table.Columns[0]
		}

	}
}

func (s *sTable) SelectGenTableByName(ctx context.Context, tableName string) (result *model.GenTableDTO, err error) {
	err = dao.GenTable.Ctx(ctx).Where(dao.GenTable.Columns().TableName, tableName).Scan(&result)
	if err != nil {
		return nil, err
	}
	var columns []*entity.GenTableColumn
	err = dao.GenTableColumn.Ctx(ctx).Where(dao.GenTableColumn.Columns().TableId, result.TableId).Scan(&columns)
	if err != nil {
		return nil, err
	}
	result.Columns = columns
	s.setTableFromOptions(result)
	return result, nil

}

func (s *sTable) BatchGenCode(ctx context.Context, ids []int) (fileList []string, err error) {
	for _, id := range ids {
		files, err := s.GenCode(ctx, id)
		if err != nil {
			return nil, err
		}
		fileList = append(fileList, files...)
	}
	return
}

func (s *sTable) GenCode(ctx context.Context, id int) (fileList []string, err error) {
	table, err := s.SelectGenTableById(ctx, id)
	if err != nil {
		return nil, err
	}
	genData, err := s.generatorCode(ctx, table)
	if err != nil {
		return nil, err
	}
	for template, code := range genData {
		path := strings.Join([]string{consts.AutoPath, "/", template}, "")
		err = s.createFile(path, code, false)
		if err != nil {
			return nil, err
		}
		fileList = append(fileList, path)
	}
	return
}

// createFile 创建文件
func (s *sTable) createFile(fileName, data string, cover bool) (err error) {
	if !gfile.Exists(fileName) || cover {
		var f *os.File
		f, err = gfile.Create(fileName)
		if err != nil {
			return err
		}
		_, err = f.WriteString(data)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}

	}
	return
}
